# NetworkX入门，参考：https://blog.csdn.net/PolarisRisingWar/article/details/117372435
import networkx as nx
import matplotlib.pyplot as plt

# Graph Types：允许以可哈希的object作为节点，任何Python object作为边属性。

# 图数据的创建、属性与常用方法
# 创建无向图
G = nx.Graph()
# 创建有向图
DG = nx.DiGraph()
# 有向图转无向图
DG2G = nx.Graph(DG)
# 判断图是否是有向图
print("否是有向图", G.is_directed())

"""
添加结点:
1、直接添加一个节点（任何object都可以作为节点，包括另一个图）G.add_node(1) G.add_node(DG)
2、从任何容器加点：a list, dict, set or even the lines from a file or the nodes from another graph
G.add_nodes_from() 或 nx.path_graph()（相当于生成一条线）
"""
G.add_nodes_from([2, 3])
G.add_nodes_from(range(100, 110))
H = nx.path_graph(10)
# H里面有2，3结点，会覆盖掉[2,3]中的2，3结点
G.add_nodes_from(H)
# add_node将参数看作一个结点，adds node "spam"
G.add_node('spam')
# add_nodes_from将参数看作是一组结点，adds 4 nodes: 's', 'p', 'a', 'm'
G.add_nodes_from('spam')
print(G)
print(H)

"""
添加边：
1、添加一条边 G.add_edge(u, v)
2、添加一个边的列表 G.add_edges_from([(1, 2), (1, 3)])
3、添加一个边的collection G.add_edges_from(H.edges)
4、如果添加的边的点不存在于图中，会自动添上相应节点而不报错
"""
G.add_edges_from([(1, 2), (1, 3)])
print("添加边[(1, 2), (1, 3)]：", G)

"""
属性值：
1、图的节点/边/图都可以在关联的attribute字典中以键值对key/value形式存储attribute（key一定要是可哈希的）
2、默认情况下属性字典是空的
3、可以通过add_edge()，add_node()方法或直接操作分别名为graph，edges，nodes的属性字典来进行操作
"""
print("创建新的图，并带有属性")
# 创建图属性
G = nx.Graph(day="Friday")
print(G.graph)  # 输出：{'day': 'Friday'}

# 赋属性或修改属性
G.graph["Name"] = "Bar"
print(G.graph)  # 输出：{'Name': 'Bar'}

# 创建含属性的节点，为节点赋属性、删除节点属性，迭代输出节点及其属性数据
G.add_node(1, time="5pm")
G.add_nodes_from([3], time="2pm")
print(G.nodes[1])  # 输出：{'time': '5pm'}
# 结点属性赋值
G.nodes[1]["room"] = 714  # node must exist already to use G.nodes
del G.nodes[1]["room"]  # remove attribute
print(list(G.nodes(data=True)))  # 输出：[(1, {'time': '5pm'}), (3, {'time': '2pm'})]
print(list(G))  # G也可以作为G节点的迭代器

# 给边添加属性
# 边(1,2)添加weight属性
G.add_edge(1, 2, weight=4.7)
# 添加color属性
G.add_edges_from([(3, 4), (4, 5)], color="red")
G.add_edges_from([(1, 2, {"color": "blue"}), (2, 3, {"weight": 8})])
G[1][2]["weight"] = 4.7
G.edges[1, 2]["weight"] = 4
G.edges[(1, 2)]["weight"] = 4
# 跟上面的G.nodes()类似
print("边的集合", list(G.edges(data=True)))
# 注意：G.edges属性是只读的，但是可以更改类似G.edges[1, 2]这种object里的属性，举例：G.edges[1, 2]['weight'] = 4，
# （对multigraph：MG.edges[u, v, key][name] = value）

# iterate through nodes
# nodes = [n for n in G if n < 3]
# print(nodes)
# number of nodes in graph
print("G的结点个数", len(G))

"""
结点数量和边数量
"""
print("结点数量", G.number_of_nodes())
print("边的数量", G.number_of_edges())

"""
可视化
"""
# G.add_edge(2, 5)
# # 绘制图形
# nx.draw(G, with_labels=True)
# # 展示图形
# plt.show()

"""
结点特征
1、度数：G.degree[node_id]，G.out_degree(node_id)，G.in_degree(node_id)
可选参数：weight
2、邻居：
    1、无向图：G.neighbors(node_id) 返回迭代器
    2、有向图：
        1、G.successors(node_id)（node_id指向的节点）
        2、G.predecessors(node_id)（指向node_id的节点）
    另一种访问邻居的方式是通过G.adj（adjacency list，详情见后文），G[node_id]，G.adj[node_id]
3、PageRank：nx.pagerank(G) 返回PageRank向量 pagerank()文档
"""

"""
删除数据
1、删除节点：G.remove_node(n)，传入要删除的结点
2、删除边:G.remove_edge(u,v)
3、删除所有节点和边：G.clear()
"""

"""
图表示
adjacency list
    1、G.adj，返回值示例：AdjacencyView({1: {2: {'weight': 0.125}, 3: {'weight': 0.75}}, 2: {1: {'weight': 0.125}, 4: {'weight': 1.2}}, 3: {1: {'weight': 0.75}, 4: {'weight': 0.375}}, 4: {2: {'weight': 1.2}, 3: {'weight': 0.375}}})
    2、G.adjacency()，返回一个迭代器，每个元素示例格式：(1, {2: {'weight': 0.125}, 3: {'weight': 0.75}})
    3、用类似 G[1] 的方法也可以访问（相当于 G.adj[1]），返回值示例：AtlasView({2: {'weight': 0.125}, 3: {'weight': 0.75}})
"""
print(G.adj)